package com.realtyreg.service.jpa;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.encoding.PasswordEncoder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.realtyreg.domain.entity.users.Profile;
import com.realtyreg.domain.entity.users.User;
import com.realtyreg.domain.entity.users.UserRole;
import com.realtyreg.repository.ProfileRepository;
import com.realtyreg.repository.UserRepository;
import com.realtyreg.service.UserService;
import com.realtyreg.service.form.Registration;
import com.realtyreg.service.mail.MailSenderService;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @author Petro
 * @created 05.09.2012 22:07
 */
@Service("userService")
@Repository
@Transactional(rollbackFor = java.lang.Exception.class)
public class UserServiceImpl implements UserService, UserDetailsService {
    @Autowired
    private UserRepository userRepository;
    @Autowired
    private ProfileRepository profileRepository;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private MailSenderService mailSenderService;

    private static final int CONFIRM_TOKEN_LENGTH = 16;

    @Transactional(readOnly = true)
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        if (StringUtils.isEmpty(username))
            throw new UsernameNotFoundException("");
        User user = userRepository.findByEmail(username);
        if (user == null)
            throw new UsernameNotFoundException("");
        return user;
    }

    @Transactional(readOnly = true)
    public User findByEmail(String email) {
        return userRepository.findByEmail(email);
    }

    public User create(Registration registration, UserRole ... roles) {
        String email = registration.getEmail();
        if (userExists(email))
            throw new IllegalArgumentException("Такой email уже занят");

        User user = new User();
        user.setEmail(email);
        user.setPassword(passwordEncoder.encodePassword(registration.getPasswordForm().getNewPassword(), email));
        user.setEnabled(true);
        user.setConfirmed(false);
        user.setConfirmToken(RandomStringUtils.randomAlphanumeric(CONFIRM_TOKEN_LENGTH));

        UserRole role = Enum.valueOf(UserRole.class, registration.getRole());
        switch (role) {
            case INDIVIDUAL:
            case ADMIN:
            case EMPLOYEE:
                user.setSurName(registration.getNameForm().getSurName());
                user.setFirstName(registration.getNameForm().getFirstName());
                user.setPatrName(registration.getNameForm().getPatrName());
                break;
            case AGENCY:
                user.setName(registration.getNameForm().getName());
                break;
        }

        //добавляем роли
        Set<UserRole> userRoles = new HashSet<UserRole>();
        userRoles.add(role); //из регистрации
        if (ArrayUtils.isNotEmpty(roles))
            userRoles.addAll(Arrays.asList(roles));
        user.setRoles(userRoles);
        userRepository.save(user);

        //создаем профиль
        Profile profile = new Profile(user);
        profileRepository.save(profile);

        //отправляем письмо для подтверждения регистрации
        mailSenderService.sendRegistrationConfirmMail(email, user.getId(), user.getConfirmToken());

        return user;
    }

    public User createNewPasswordToken(User user) {
        user.setConfirmToken(RandomStringUtils.randomAlphanumeric(CONFIRM_TOKEN_LENGTH));
        mailSenderService.sendRecoverPasswordMail(user.getEmail(), user.getId(), user.getConfirmToken());
        return save(user);
    }

    public User saveNewPassword(User user, String password) {
        user.setPassword(passwordEncoder.encodePassword(password, user.getEmail()));
        user.setConfirmToken(null);
        return save(user);
    }

    @Transactional(readOnly = true)
    public boolean userExists(String email) {
        return userRepository.findByEmail(email) != null;
    }

    @Transactional(readOnly = true)
    public User findOne(Long id) {
        return userRepository.findOne(id);
    }

    @Transactional(readOnly = true)
    public List<User> findAll() {
        return userRepository.findAll();
    }

    public User save(User user) {
        return userRepository.save(user);
    }

    public void delete(Long id) {
        userRepository.delete(id);
    }

    @Transactional(readOnly = true)
    public Profile findProfile(Long id) {
        return profileRepository.findByUserId(id);
    }

    public Profile updateProfile(Profile profile) {
        return profileRepository.save(profile);
    }
}
